#include "wx/wxprec.h"
#include "wx/font.h"

#ifndef WX_PRECOMP
#include "wx/dc.h"
#include "wx/intl.h"
#include "wx/dcscreen.h"
#include "wx/log.h"
#include "wx/gdicmn.h"
#endif // WX_PRECOMP

#include "wx/fontutil.h" // for wxNativeFontInfo
#include "wx/fontmap.h"
#include "wx/fontenum.h"
#include "wx/tokenzr.h"

static void AdjustFontSize( wxFont& font, wxDC& dc, const wxSize& pixelSize ) {
  int currentSize = 0;
  int largestGood = 0;
  int smallestBad = 0;
  bool initialGoodFound = false;
  bool initialBadFound = false;
  currentSize = font.GetPointSize();
  while( currentSize > 0 ) {
    dc.SetFont( font );
    if( dc.GetCharHeight() <= pixelSize.GetHeight() &&
        ( !pixelSize.GetWidth() ||
          dc.GetCharWidth() <= pixelSize.GetWidth() ) ) {
      largestGood = currentSize;
      initialGoodFound = true;
    } else {
      smallestBad = currentSize;
      initialBadFound = true;
    }
    if( !initialGoodFound ) {
      currentSize /= 2;
    } else if( !initialBadFound ) {
      currentSize *= 2;
    } else {
      int distance = smallestBad - largestGood;
      if( distance == 1 ) {
        break;
      }
      currentSize = largestGood + distance / 2;
    }
    font.SetPointSize( currentSize );
  }
  if( currentSize != largestGood ) {
    font.SetPointSize( largestGood );
  }
}

wxFontEncoding wxFontBase::ms_encodingDefault = wxFONTENCODING_SYSTEM;

void wxFontBase::SetDefaultEncoding( wxFontEncoding encoding ) {
  wxCHECK_RET( encoding != wxFONTENCODING_DEFAULT,
               _T( "can't set default encoding to wxFONTENCODING_DEFAULT" ) );
  ms_encodingDefault = encoding;
}

wxFontBase::~wxFontBase() {
}

wxFont *wxFontBase::New( int size,
                         int family,
                         int style,
                         int weight,
                         bool underlined,
                         const wxString& face,
                         wxFontEncoding encoding ) {
  return new wxFont( size, family, style, weight, underlined, face, encoding );
}

static inline int flags2Style( int flags ) {
  return flags & wxFONTFLAG_ITALIC
         ? wxFONTSTYLE_ITALIC
         : flags & wxFONTFLAG_SLANT
         ? wxFONTSTYLE_SLANT
         : wxFONTSTYLE_NORMAL;
}

static inline int flags2Weight( int flags ) {
  return flags & wxFONTFLAG_LIGHT
         ? wxFONTWEIGHT_LIGHT
         : flags & wxFONTFLAG_BOLD
         ? wxFONTWEIGHT_BOLD
         : wxFONTWEIGHT_NORMAL;
}

static inline bool flags2Underlined( int flags ) {
  return ( flags & wxFONTFLAG_UNDERLINED ) != 0;
}

wxFont *wxFontBase::New( int pointSize,
                         wxFontFamily family,
                         int flags,
                         const wxString& face,
                         wxFontEncoding encoding ) {
  return New( pointSize, family, flags2Style( flags ), flags2Weight( flags ),
              flags2Underlined( flags ), face, encoding );
}

wxFont *wxFontBase::New( const wxSize& pixelSize,
                         int family,
                         int style,
                         int weight,
                         bool underlined,
                         const wxString& face,
                         wxFontEncoding encoding ) {
  wxFont *self = New( 10, family, style, weight, underlined, face, encoding );
  wxScreenDC dc;
  AdjustFontSize( *( wxFont * )self, dc, pixelSize );
  return self;
}

wxFont *wxFontBase::New( const wxSize& pixelSize,
                         wxFontFamily family,
                         int flags,
                         const wxString& face,
                         wxFontEncoding encoding ) {
  return New( pixelSize, family, flags2Style( flags ), flags2Weight( flags ),
              flags2Underlined( flags ), face, encoding );
}

wxSize wxFontBase::GetPixelSize() const {
  wxScreenDC dc;
  dc.SetFont( *( wxFont * )this );
  return wxSize( dc.GetCharWidth(), dc.GetCharHeight() );
}

bool wxFontBase::IsUsingSizeInPixels() const {
  return false;
}

void wxFontBase::SetPixelSize( const wxSize& pixelSize ) {
  wxScreenDC dc;
  AdjustFontSize( *( wxFont * )this, dc, pixelSize );
}

/* static */
wxFont *wxFontBase::New( const wxNativeFontInfo& info ) {
  return new wxFont( info );
}

/* static */
wxFont *wxFontBase::New( const wxString& strNativeFontDesc ) {
  wxNativeFontInfo fontInfo;
  if( !fontInfo.FromString( strNativeFontDesc ) ) {
    return new wxFont( *wxNORMAL_FONT );
  }
  return New( fontInfo );
}

bool wxFontBase::IsFixedWidth() const {
  return GetFamily() == wxFONTFAMILY_TELETYPE;
}

void wxFontBase::DoSetNativeFontInfo( const wxNativeFontInfo& info ) {
  #ifdef wxNO_NATIVE_FONTINFO
  SetPointSize( info.pointSize );
  SetFamily( info.family );
  SetStyle( info.style );
  SetWeight( info.weight );
  SetUnderlined( info.underlined );
  SetFaceName( info.faceName );
  SetEncoding( info.encoding );
  #else
  ( void )info;
  #endif
}

wxString wxFontBase::GetNativeFontInfoDesc() const {
  wxString fontDesc;
  const wxNativeFontInfo *fontInfo = GetNativeFontInfo();
  if( fontInfo ) {
    fontDesc = fontInfo->ToString();
    wxASSERT_MSG( !fontDesc.empty(), wxT( "This should be a non-empty string!" ) );
  } else
  { wxFAIL_MSG( wxT( "Derived class should have created the wxNativeFontInfo!" ) ); }
  return fontDesc;
}

wxString wxFontBase::GetNativeFontInfoUserDesc() const {
  wxString fontDesc;
  const wxNativeFontInfo *fontInfo = GetNativeFontInfo();
  if( fontInfo ) {
    fontDesc = fontInfo->ToUserString();
    wxASSERT_MSG( !fontDesc.empty(), wxT( "This should be a non-empty string!" ) );
  } else
  { wxFAIL_MSG( wxT( "Derived class should have created the wxNativeFontInfo!" ) ); }
  return fontDesc;
}

bool wxFontBase::SetNativeFontInfo( const wxString& info ) {
  wxNativeFontInfo fontInfo;
  if( !info.empty() && fontInfo.FromString( info ) ) {
    SetNativeFontInfo( fontInfo );
    return true;
  }
  UnRef();
  return false;
}

bool wxFontBase::SetNativeFontInfoUserDesc( const wxString& info ) {
  wxNativeFontInfo fontInfo;
  if( !info.empty() && fontInfo.FromUserString( info ) ) {
    SetNativeFontInfo( fontInfo );
    return true;
  }
  UnRef();
  return false;
}

bool wxFontBase::operator==( const wxFont& font ) const {
  // either it is the same font, i.e. they share the same common data or they
  // have different ref datas but still describe the same font
  return IsSameAs( font ) ||
         (
           Ok() == font.Ok() &&
           GetPointSize() == font.GetPointSize() &&
           // in wxGTK1 GetPixelSize() calls GetInternalFont() which uses
           // operator==() resulting in infinite recursion so we can't use it
           // in that port
           #if !defined(__WXGTK__) || defined(__WXGTK20__)
           GetPixelSize() == font.GetPixelSize() &&
           #endif
           GetFamily() == font.GetFamily() &&
           GetStyle() == font.GetStyle() &&
           GetWeight() == font.GetWeight() &&
           GetUnderlined() == font.GetUnderlined() &&
           GetFaceName().IsSameAs( font.GetFaceName(), false ) &&
           GetEncoding() == font.GetEncoding()
         );
}

bool wxFontBase::operator!=( const wxFont& font ) const {
  return !( *this == font );
}

wxString wxFontBase::GetFamilyString() const {
  wxCHECK_MSG( Ok(), wxT( "wxDEFAULT" ), wxT( "invalid font" ) );
  switch( GetFamily() ) {
    case wxDECORATIVE:
      return wxT( "wxDECORATIVE" );
    case wxROMAN:
      return wxT( "wxROMAN" );
    case wxSCRIPT:
      return wxT( "wxSCRIPT" );
    case wxSWISS:
      return wxT( "wxSWISS" );
    case wxMODERN:
      return wxT( "wxMODERN" );
    case wxTELETYPE:
      return wxT( "wxTELETYPE" );
    default:
      return wxT( "wxDEFAULT" );
  }
}

wxString wxFontBase::GetStyleString() const {
  wxCHECK_MSG( Ok(), wxT( "wxDEFAULT" ), wxT( "invalid font" ) );
  switch( GetStyle() ) {
    case wxNORMAL:
      return wxT( "wxNORMAL" );
    case wxSLANT:
      return wxT( "wxSLANT" );
    case wxITALIC:
      return wxT( "wxITALIC" );
    default:
      return wxT( "wxDEFAULT" );
  }
}

wxString wxFontBase::GetWeightString() const {
  wxCHECK_MSG( Ok(), wxT( "wxDEFAULT" ), wxT( "invalid font" ) );
  switch( GetWeight() ) {
    case wxNORMAL:
      return wxT( "wxNORMAL" );
    case wxBOLD:
      return wxT( "wxBOLD" );
    case wxLIGHT:
      return wxT( "wxLIGHT" );
    default:
      return wxT( "wxDEFAULT" );
  }
}

bool wxFontBase::SetFaceName( const wxString &facename ) {
  if( !wxFontEnumerator::IsValidFacename( facename ) ) {
    UnRef();        // make Ok() return false
    return false;
  }
  return true;
}


// ----------------------------------------------------------------------------
// wxNativeFontInfo
// ----------------------------------------------------------------------------

// Up to now, there are no native implementations of this function:
void wxNativeFontInfo::SetFaceName( const wxArrayString &facenames ) {
  for( size_t i = 0; i < facenames.GetCount(); i++ ) {
    if( wxFontEnumerator::IsValidFacename( facenames[i] ) ) {
      SetFaceName( facenames[i] );
      return;
    }
  }
  // set the first valid facename we can find on this system
  wxString validfacename = wxFontEnumerator::GetFacenames().Item( 0 );
  wxLogTrace( wxT( "font" ), wxT( "Falling back to '%s'" ), validfacename.c_str() );
  SetFaceName( validfacename );
}


#ifdef wxNO_NATIVE_FONTINFO

// These are the generic forms of FromString()/ToString.
//
// convert to/from the string representation: format is
//      version;pointsize;family;style;weight;underlined;facename;encoding

bool wxNativeFontInfo::FromString( const wxString& s ) {
  long l;
  wxStringTokenizer tokenizer( s, _T( ";" ) );
  wxString token = tokenizer.GetNextToken();
  //
  //  Ignore the version for now
  //
  token = tokenizer.GetNextToken();
  if( !token.ToLong( &l ) ) {
    return false;
  }
  pointSize = ( int )l;
  token = tokenizer.GetNextToken();
  if( !token.ToLong( &l ) ) {
    return false;
  }
  family = ( wxFontFamily )l;
  token = tokenizer.GetNextToken();
  if( !token.ToLong( &l ) ) {
    return false;
  }
  style = ( wxFontStyle )l;
  token = tokenizer.GetNextToken();
  if( !token.ToLong( &l ) ) {
    return false;
  }
  weight = ( wxFontWeight )l;
  token = tokenizer.GetNextToken();
  if( !token.ToLong( &l ) ) {
    return false;
  }
  underlined = l != 0;
  faceName = tokenizer.GetNextToken();
  #ifndef __WXMAC__
#error mmmm
  if( !faceName ) {
    return false;
  }
  #endif
  token = tokenizer.GetNextToken();
  if( !token.ToLong( &l ) ) {
    return false;
  }
  encoding = ( wxFontEncoding )l;
  return true;
}

wxString wxNativeFontInfo::ToString() const {
  wxString s;
  s.Printf( _T( "%d;%d;%d;%d;%d;%d;%s;%d" ), 0, pointSize, family, ( int )style, ( int )weight, underlined, faceName.GetData(), ( int )encoding );
  return s;
}

void wxNativeFontInfo::Init() {
  pointSize = 0;
  family = wxFONTFAMILY_DEFAULT;
  style = wxFONTSTYLE_NORMAL;
  weight = wxFONTWEIGHT_NORMAL;
  underlined = false;
  faceName.clear();
  encoding = wxFONTENCODING_DEFAULT;
}

int wxNativeFontInfo::GetPointSize() const {
  return pointSize;
}

wxFontStyle wxNativeFontInfo::GetStyle() const {
  return style;
}

wxFontWeight wxNativeFontInfo::GetWeight() const {
  return weight;
}

bool wxNativeFontInfo::GetUnderlined() const {
  return underlined;
}

wxString wxNativeFontInfo::GetFaceName() const {
  return faceName;
}

wxFontFamily wxNativeFontInfo::GetFamily() const {
  return family;
}

wxFontEncoding wxNativeFontInfo::GetEncoding() const {
  return encoding;
}

void wxNativeFontInfo::SetPointSize( int pointsize ) {
  pointSize = pointsize;
}

void wxNativeFontInfo::SetStyle( wxFontStyle style_ ) {
  style = style_;
}

void wxNativeFontInfo::SetWeight( wxFontWeight weight_ ) {
  weight = weight_;
}

void wxNativeFontInfo::SetUnderlined( bool underlined_ ) {
  underlined = underlined_;
}

bool wxNativeFontInfo::SetFaceName( const wxString& facename_ ) {
  faceName = facename_;
  return true;
}

void wxNativeFontInfo::SetFamily( wxFontFamily family_ ) {
  family = family_;
}

void wxNativeFontInfo::SetEncoding( wxFontEncoding encoding_ ) {
  encoding = encoding_;
}
#error mmmmm

#endif // generic wxNativeFontInfo implementation

#if defined(wxNO_NATIVE_FONTINFO) || defined(__WXMSW__) || defined (__WXPM__)
#error mmmmm
wxString wxNativeFontInfo::ToUserString() const {
  wxString desc;
  // first put the adjectives, if any - this is English-centric, of course,
  // but what else can we do?
  if( GetUnderlined() ) {
    desc << _( "underlined" );
  }
  switch( GetWeight() ) {
    default:
      wxFAIL_MSG( _T( "unknown font weight" ) );
    // fall through
    case wxFONTWEIGHT_NORMAL:
      break;
    case wxFONTWEIGHT_LIGHT:
      desc << _( " light" );
      break;
    case wxFONTWEIGHT_BOLD:
      desc << _( " bold" );
      break;
  }
  switch( GetStyle() ) {
    default:
      wxFAIL_MSG( _T( "unknown font style" ) );
    case wxFONTSTYLE_NORMAL:
      break;
    case wxFONTSTYLE_ITALIC:
    case wxFONTSTYLE_SLANT:
      desc << _( " italic" );
      break;
  }
  wxString face = GetFaceName();
  if( !face.empty() ) {
    desc << _T( ' ' ) << face;
  }
  int size = GetPointSize();
  if( size != wxNORMAL_FONT->GetPointSize() ) {
    desc << _T( ' ' ) << size;
  }
  wxFontEncoding enc = GetEncoding();
  if( enc != wxFONTENCODING_DEFAULT && enc != wxFONTENCODING_SYSTEM ) {
    desc << _T( ' ' ) << wxFontMapper::GetEncodingName( enc );
  }
  return desc.Strip( wxString::both ).MakeLower();
}

bool wxNativeFontInfo::FromUserString( const wxString& s ) {
  Init();
  wxStringTokenizer tokenizer( s, _T( ";, " ), wxTOKEN_STRTOK );
  wxString face;
  unsigned long size;
  bool weightfound = false, pointsizefound = false;
  bool encodingfound = false;
  while( tokenizer.HasMoreTokens() ) {
    wxString token = tokenizer.GetNextToken();
    // normalize it
    token.Trim( true ).Trim( false ).MakeLower();
    // look for the known tokens
    if( token == _T( "underlined" ) || token == _( "underlined" ) ) {
      SetUnderlined( true );
    } else if( token == _T( "light" ) || token == _( "light" ) ) {
      SetWeight( wxFONTWEIGHT_LIGHT );
      weightfound = true;
    } else if( token == _T( "bold" ) || token == _( "bold" ) ) {
      SetWeight( wxFONTWEIGHT_BOLD );
      weightfound = true;
    } else if( token == _T( "italic" ) || token == _( "italic" ) ) {
      SetStyle( wxFONTSTYLE_ITALIC );
    } else if( token.ToULong( &size ) ) {
      SetPointSize( size );
      pointsizefound = true;
    } else {
      wxFontEncoding encoding = wxFontMapper::Get()->CharsetToEncoding( token, false );
      if( encoding != wxFONTENCODING_DEFAULT &&
          encoding != wxFONTENCODING_SYSTEM ) {  // returned when the recognition failed
        SetEncoding( encoding );
        encodingfound = true;
      } else {
        if( !face.empty() ) {
          face += _T( ' ' );
        }
        face += token;
        continue;
      }
    }
    if( !face.empty() ) {
      if( !wxFontEnumerator::IsValidFacename( face ) ||
          !SetFaceName( face ) ) {
        SetFaceName( wxNORMAL_FONT->GetFaceName() );
      }
      face.clear();
    }
  }
  // we might not have flushed it inside the loop
  if( !face.empty() ) {
    if( !wxFontEnumerator::IsValidFacename( face ) ||
        !SetFaceName( face ) ) {
      SetFaceName( wxNORMAL_FONT->GetFaceName() );
    }
  }
  // set point size to default value if size was not given
  if( !pointsizefound ) {
    SetPointSize( wxNORMAL_FONT->GetPointSize() );
  }
  // set font weight to default value if weight was not given
  if( !weightfound ) {
    SetWeight( wxFONTWEIGHT_NORMAL );
  }
  if( !encodingfound ) {
    SetEncoding( wxFONTENCODING_SYSTEM );
  }
  return true;
}

#endif // generic or wxMSW or wxOS2
